#ifndef SEG_H
#define SEG_H

#include <stdint.h>
#include <string.h>

#define NONE_SEG 0

typedef uint32_t seg_t;
typedef uint32_t seg_point_t;

static inline seg_point_t start_seg(seg_t seg)
{
	return (seg & 0xffff);
}

static inline seg_point_t end_seg(seg_t seg)
{
	return (seg >> 16) ;
}

static inline int valid_seg(seg_t seg)
{
	return end_seg(seg) > start_seg(seg);
}

static inline seg_t make_seg(seg_point_t start, seg_point_t end)
{
	return (start & 0xffff) + (end << 16);
}

static inline int len_of_seg(seg_t seg)
{
	return end_seg(seg) - start_seg(seg);
}

#define SEG_LIMIT 64

typedef struct seg_list {
	seg_t segs[SEG_LIMIT];
	seg_t segs_ptr;
} seg_list_t;

#define SEG_LIST(segs) \
	struct seg_list segs = {.segs_ptr = NONE_SEG}

static inline void seg_list_init(struct seg_list *seg_list)
{
	seg_list->segs_ptr = NONE_SEG;
}

static inline int seg_list_push(struct seg_list *seg_list, seg_t seg)
{
	seg_point_t start = start_seg(seg_list->segs_ptr);
	seg_point_t end = end_seg(seg_list->segs_ptr);
	if (end >= SEG_LIMIT)
		return -1;
	seg_list->segs[end++] = seg;
	seg_list->segs_ptr = make_seg(start, end);
	return 0;
}

static inline seg_t seg_list_pop(struct seg_list *seg_list)
{
	seg_point_t start = start_seg(seg_list->segs_ptr);
	seg_point_t end = end_seg(seg_list->segs_ptr);
	seg_t seg;
	if (end > start)
		seg = seg_list->segs[--end];
	else 
		seg = NONE_SEG;
	seg_list->segs_ptr = make_seg(start, end);
	return seg;
}

static inline seg_t seg_list_pop_front(struct seg_list *seg_list)
{
	seg_t seg;
	seg_point_t start = start_seg(seg_list->segs_ptr);
	seg_point_t end = end_seg(seg_list->segs_ptr);
	if (end > start)
		seg = seg_list->segs[start++];
	else 
		seg = NONE_SEG;
	seg_list->segs_ptr = make_seg(start, end);
	return seg;
}


static inline int seg_list_size(struct seg_list *seg_list)
{
	return len_of_seg(seg_list->segs_ptr);
}

static inline seg_t seg_list_front(struct seg_list *seg_list)
{
	seg_point_t start = start_seg(seg_list->segs_ptr);
	seg_point_t end = end_seg(seg_list->segs_ptr);
	if (start < end)
		return seg_list->segs[start];
	else
		return NONE_SEG;
}

static inline seg_t seg_list_back(struct seg_list *seg_list)
{
	seg_point_t start = start_seg(seg_list->segs_ptr);
	seg_point_t end = end_seg(seg_list->segs_ptr);
	if (start < end)
		return seg_list->segs[end - 1];
	else
		return NONE_SEG;
}

static inline void copy_seg(struct seg_list *dest, struct seg_list *src)
{
	memcpy(dest, src, sizeof(struct seg_list));
}

typedef int (*seg_list_call_t)(seg_t seg, const void *userdata);

static inline void foreach_seg(struct seg_list *segs, seg_list_call_t call, const void *userdata)
{
	seg_point_t start = start_seg(segs->segs_ptr);
	seg_point_t end = end_seg(segs->segs_ptr);
	for (;start < end; start ++) {
		call(segs->segs[start], userdata);
	}
}

#endif
